package nmap

import (
	"fmt"
	"strings"
)

// 部分规则来源于 https://github.com/gelim/nmap-erpscan/blob/master/nmap-service-probes, 主要合并了payload 相同的部分
// 对于一些需要再发 HTTP Request 没处理如 q|GET /v2/info HTTP/1.0\r\nHost: 127.0.0.1:30030\r\n\r\n|

var Patches = []*ProbePatch{
	// 一条十分宽松的 ssh 规则
	{
		Name: ProbeNULL,
		Mode: PatchModeModify,
		Apply: func(p *Probe) error {
			m, err := ParserMatchLine(`match ssh m/^(SSH-.*)/ p/SSH/ i/$1/`)
			if err != nil {
				return err
			}
			p.Matches = append(p.Matches, m)
			return nil
		},
	},
	{
		Name: "GetRequest",
		Mode: PatchModeModify,
		Apply: func(p *Probe) error {
			for _, line := range []string{
				`match sapstartservice m|SID=(\w+)&NR=(\d+)&HOST=(\S+)\r\nServer: gSOAP| p/SAP Management Console/ i/SID $1, NR $2/ h/$3/`,
				`match sapstartservice m|Location: /sapmc/sapmc\.html\r\nServer: gSOAP| p/SAP Management Console/`,
				`match saphostcontrol m|HOST=(\S+)\r\nServer: gSOAP| p/SAPHostControl/ h/$1/`,

				`match sapmshttp m|server: SAP Message Server, release (\d+) \((\w+)\)| p/SAP Message Server httpd/ v/release $1/ i/SID $2/`,
				`match sapjavaweb m|SAP J2EE Engine/([\d.]+)| p/SAP JAVA EE Dispatcher HTTP/ v/$1/`,
				`match sapjavaweb m|SAP J2EE Engine| p/SAP JAVA EE Dispatcher HTTP/`,
				`match sapwebapp m|sap-system: (\w+).*SAP Web Application Server \(.*?\)| p/SAP Web Application Server/ v/$1/`,
				`match sapjavaweb m|SAP NetWeaver Application Server ([\d.]+) / AS Java ([\d.]+)| p/SAP NetWeaver Application Server/ i/Kernel version $1, Java version $2/`,
				`match sapicm m|SAP NetWeaver Application Server ([\d.]+) / ICM ([\d.]+)| p/SAP Internet Communication Manager/ v/$2/`,
				`match sapnetweaver m|SAP NetWeaver Application Server \(([\d.]+);([\d.]+)\)| p/SAP Internet Communication Manager/ v/$2/`,
				`match sapicm m|SAP Internet Communication Framework| p/SAP Internet Communication Manager/`,
				// `match sapicm m|<H2><b>403 Access denied</b></H2><br>You do not have the permissions to access this resource<br>| p/SAP Internet Communication Manager/`,
				`match sapwebas m|SAP Web Application Server| p/SAP Web Application Server/`,
				`match saphttpmsgserv m|msg_server \((\w+)\)| p/SAP HTTP Message Server/ i/SID $1/`,
				`match sapwebmobile m|SAP Mobile Platform| p/SAP Mobile Platform/`,
				`match sapsmtp m|(\S+) SAP (\S+) E?SMTP service ready| p/SAP SMTP Server/ h/$1/ v/$2/`,
				`match sapxscontroller m|SAP SE| p/SAP XSA Controller/`,

				`match saphanaxs m|XSEngine| p/SAP HANA XS Engine/`,
				// `match saptrex m|Server: ([\w./ ]+)| p/SAP TREX AlertServer/ i/$1/`,
				`match sapmobile m|WWW-Authenticate: Basic realm="Unwired Platform"| p/SAP Mobile Platform/`,

				// 这一条一定要放在最后, 否则逻辑会提前结束
				`softmatch http m|^HTTP/[^ ]{1,3} \d{3}|i`,
			} {
				m, err := ParserMatchLine(line)
				if err != nil {
					return err
				}
				p.Matches = append(p.Matches, m)
			}

			// 添加 rabbitmq 详情, 从之前的 probe 迁移过来的
			p.Ports.AddTCPPorts(5672)
			for _, m := range p.Matches {
				if m.ServiceName == "amqp" {
					if m.Rule.String() == `^AMQP\x00\x00\x09\x01$` {
						m.CPE = []string{"cpe:/a:*:amqp", "cpe:/a:*:rabbitmq/"}
					} else if m.Rule.String() == `^AMQP\x01\x01\x00\x0a$` {
						m.CPE = []string{"cpe:/a:*:amqp", "cpe:/a:*:rabbitmq/"}
					}
				}
			}
			return nil
		},
	},
	{
		Name: "GenericLines",
		Mode: PatchModeModify,
		Apply: func(p *Probe) error {
			for _, line := range []string{
				`match sapjavatelnet m|\xff\xfb\x01\xff\xfb\x03\xff\xfd\x1f| p/SAP JAVA Telnet/`,
				`match saplogviewer m|^READY#Logviewer#([\d.]+)\r\n| p/SAP NetWeaver Logviewer/ v/$1/ cpe:/a:sap:netweaver_logviewer:$1/`,
			} {
				m, err := ParserMatchLine(line)
				if err != nil {
					return err
				}
				p.Matches = append(p.Matches, m)
			}
			return nil
		},
	},
	{
		Name: "Memcache",
		Mode: PatchModeModify,
		Apply: func(p *Probe) error {
			p.Rarity = 3
			return nil
		},
	},
	{
		Name: "oracle-tns",
		Mode: PatchModeModify,
		Apply: func(p *Probe) error {
			p.Rarity = 3
			for _, m := range p.Matches {
				if m.Product == "Oracle TNS Listener" && m.Version == "$2" {
					m.CPE = []string{"cpe:/a:oracle:database_server:$2/"}
				}
			}
			return nil
		},
	},
	{
		Name: "redis-server",
		Mode: PatchModeModify,
		Apply: func(p *Probe) error {
			p.Rarity = 3
			if len(p.Matches) != 2 {
				return fmt.Errorf("no enough match")
			}
			if p.Matches[0].Rule.String() != `-ERR operation not permitted\r\n` {
				return fmt.Errorf("probe changes %s", p.Matches[0].Rule)
			}
			if p.Matches[1].Rule.String() != `^\$\d+\r\n(?:#[^\r\n]*\r\n)*redis_version:([.\d]+)\r\n` {
				return fmt.Errorf("probe changes %s", p.Matches[1].Rule)
			}
			p.Matches[0].CPE = []string{"cpe:/a:*:redis/"}
			p.Matches[1].CPE = []string{"cpe:/a:*:redis:$1/"}
			return nil
		},
	},
	{
		Name: "*",
		Mode: PatchModeModify,
		Apply: func(p *Probe) error {
			for _, match := range p.Matches {
				if match.DeviceType == "" {
					if strings.Contains(strings.ToLower(match.Product), "hikvision") {
						match.DeviceType = "webcam"
						continue
					}
				}
				// 防止因为某些包含igor_sysoev的nginx的cpe在漏洞库中匹配不到
				for _, cpe := range match.CPE {
					if strings.Contains(cpe, "igor_sysoev") {
						match.CPE = append(match.CPE, strings.ReplaceAll(cpe, "igor_sysoev", "nginx"))
					}
				}
			}
			return nil
		},
	},
}

const (
	PatchModeAdd = iota
	PatchModeModify
)

type ProbePatch struct {
	Name  string
	Apply func(p *Probe) error
	Mode  int
}
